package com.talkweb.ncfw.security.springsecurity;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.cache.NullUserCache;

import com.talkweb.ncfw.component.rights.service.ILoginManager;
import com.talkweb.ncfw.exceptions.BizBaseException;
import com.talkweb.ncfw.utils.AssertUtils;
import com.talkweb.ncfw.utils.encrypt.RSAUtils;

/**
 * 本地化认证提供者
 * 
 * @author Wuqingming
 * 
 */
public class MyAuthenticationProvider implements AuthenticationProvider,
		InitializingBean, MessageSourceAware {
	private static final Log log = LogFactory.getLog(MyAuthenticationProvider.class);

	protected MessageSourceAccessor messages = SpringSecurityMessageSource
			.getAccessor();
	private UserCache userCache = new NullUserCache();
	private boolean forcePrincipalAsString = false;
	protected boolean hideUserNotFoundExceptions = true;
	private UserDetailsService userDetailsService;
	private ILoginManager loginManager;

	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();
		boolean cacheWasUsed = true;
		UserDetails user = this.userCache.getUserFromCache(username);
		if (user == null) {
			cacheWasUsed = false;
			try {
				user = retrieveUser(username, authentication.getCredentials()
						.toString());
			} catch (UsernameNotFoundException notFound) {
				if (hideUserNotFoundExceptions) {
					throw new BadCredentialsException(
							messages.getMessage(
									"AbstractUserDetailsAuthenticationProvider.badCredentials",
									"Bad credentials"));
				} else {
					throw notFound;
				}
			} catch (BadCredentialsException e) {	//错误的凭证（如密码凭证）
				throw e;
			} catch (AccountExpiredException e) {	//帐号失效
				throw e;
				
			} catch (DisabledException e) {			//帐号禁用
				throw e;
			}

		}
		if (!cacheWasUsed) {
			this.userCache.putUserInCache(user);
		}
		Object principalToReturn = user;
		if (forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}
		return createSuccessAuthentication(principalToReturn, authentication,
				user);
	}

	private Authentication createSuccessAuthentication(
			Object principalToReturn, Authentication authentication,
			UserDetails user) {
		UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
				principalToReturn, authentication.getCredentials(),
				user.getAuthorities());
		result.setDetails(authentication.getDetails());
		return result;
	}

	private UserDetails retrieveUser(String username, String password) throws AuthenticationException {
		UserDetails loadedUser = null;

		/** 传递过来的密码解密方式 */
		String pwd = RSAUtils.decryptStringByJs(password);
		
		try {
			int authResult = this.loginManager.authentication(username, pwd, ILoginManager.AuthType.LOGINID);
			if (authResult == -4) {
				throw new AccountExpiredException(username);
			} else if (authResult == -3) {
				throw new DisabledException(username);
			} else if (authResult == -2) {
				throw new UsernameNotFoundException(username);
			} else if (authResult == -1) {
				throw new BadCredentialsException(username);
			} 
		} catch (BizBaseException e1) {
			log.error("登录认证时发生异常", e1);
			throw new BadCredentialsException(username);
		}
		loadedUser = this.userDetailsService.loadUserByUsername(username);
		return loadedUser;
	}

	@SuppressWarnings("rawtypes")
	public boolean supports(Class authentication) {
		return (UsernamePasswordAuthenticationToken.class
				.isAssignableFrom(authentication));
	}

	public void afterPropertiesSet() throws Exception {
		AssertUtils.notNull(loginManager, "loginManager can not be null.");
		AssertUtils.notNull(userDetailsService, "userDetailsService can not be null.");
	}

	public void setMessageSource(MessageSource messageSource) {
		this.messages = new MessageSourceAccessor(messageSource);
	}

	public void setUserDetailsService(UserDetailsService userDetailsService) {
		this.userDetailsService = userDetailsService;
	}

	public UserDetailsService getUserDetailsService() {
		return userDetailsService;
	}

	public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
		this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
	}

	public boolean isHideUserNotFoundExceptions() {
		return hideUserNotFoundExceptions;
	}

	public ILoginManager getLoginManager() {
		return loginManager;
	}

	public void setLoginManager(ILoginManager loginManager) {
		this.loginManager = loginManager;
	}
	
}
